package cn.pugle.designpattern.visitor;

import java.util.ArrayList;
import java.util.List;

interface AnimalVisitor {
    void visit(Lion l);

    void visit(Deer d);
}

/**
 * https://stackoverflow.com/questions/3930808/how-to-avoid-large-if-statements-and-instanceof
 * @author tzp
 */
public class AnimalTest1 {
    public static void main(String[] args) {
        Animal lion = new Lion("Geo");
        Animal deer1 = new Deer("D1");
        Animal deer2 = new Deer("D2");

        List<Animal> li = new ArrayList<Animal>();
        li.add(lion);
        li.add(deer1);
        li.add(deer2);

        int reps = 10000000;

        long start, elapsed;

        start = System.nanoTime();
        for (int i = 0; i < reps; i++) {
            for (Animal a : li)
                a.accept(new ActionVisitor()); // <-- Accept / visit.
        }
        elapsed = System.nanoTime() - start;
        System.out.println("Visitor took " + elapsed + " ns");

//        start = System.nanoTime();
//        for (int i = 0; i < reps; i++) {
//            for (Animal a : li) {
//                if (a instanceof Lion) {
//                    ((Lion) a).roar();
//                } else if (a instanceof Deer) {
//                    ((Deer) a).runAway();
//                }
//            }
//        }
//        elapsed = System.nanoTime() - start;
//        System.out.println("instanceof took " + elapsed + " ns");

//        start = System.nanoTime();
//        for (int i = 0; i < reps; i++) {
//            for (Animal a : li) {
//                switch (a.type) {
//                    case Animal.LION_TYPE:
//                        ((Lion) a).roar();
//                        break;
//                    case Animal.DEER_TYPE:
//                        ((Deer) a).runAway();
//                        break;
//                }
//            }
//        }
//        elapsed = System.nanoTime() - start;
//        System.out.println("type constant took " + elapsed + " ns");
    }
}

abstract class Animal {
    public static final int LION_TYPE = 0;
    public static final int DEER_TYPE = 1;
    public final int type;
    String name;

    public Animal(String name, int type) {
        this.name = name;
        this.type = type;
    }

    public abstract void accept(AnimalVisitor av); // <-- Open up for visitors.
}

class Lion extends Animal {
    public Lion(String name) {
        super(name, LION_TYPE);
    }

    public void roar() {
        // System.out.println("Roar");
    }

    public void accept(AnimalVisitor av) {
        av.visit(this); // <-- Accept and call visit.
    }
}

class Deer extends Animal {

    public Deer(String name) {
        super(name, DEER_TYPE);
    }

    public void runAway() {
        // System.out.println("Running...");
    }

    public void accept(AnimalVisitor av) {
        av.visit(this); // <-- Accept and call visit.
    }

}

class ActionVisitor implements AnimalVisitor {

    public void visit(Deer d) {
        d.runAway();
    }

    public void visit(Lion l) {
        l.roar();
    }
}